package com.jwater.core.net;

import java.util.concurrent.TimeUnit;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import jwater.io.netty.bootstrap.Bootstrap;
import jwater.io.netty.channel.ChannelFuture;
import jwater.io.netty.channel.ChannelInitializer;
import jwater.io.netty.channel.ChannelOption;
import jwater.io.netty.channel.EventLoopGroup;
import jwater.io.netty.channel.nio.NioEventLoopGroup;
import jwater.io.netty.channel.socket.SocketChannel;
import jwater.io.netty.channel.socket.nio.NioSocketChannel;
import jwater.io.netty.handler.codec.LengthFieldBasedFrameDecoder;
import jwater.io.netty.handler.timeout.IdleStateHandler;
/**
 * @description:
 * @author: jnner
 * @createDate: 2021/2/5
 * @version: 1.0
 */
public class NettyClient {
	protected static Logger logger = LoggerFactory.getLogger(NettyClient.class);

	public static boolean printLog = true;
	private EventLoopGroup group = null;
	private Bootstrap bootstrap;
	private ChannelFuture channelFuture;
	private IdleStateHandler idleStateHandler;
	private NettyClientHandler handler;
	protected SimpleNode node;
	protected NodeHandler nodeHandler;

	public void init() throws InterruptedException {
		int threadCount = Runtime.getRuntime().availableProcessors() * 2;
		threadCount = threadCount < 2 ? 2 : threadCount;
		threadCount = threadCount > 8 ? 8 : threadCount;
		group = new NioEventLoopGroup(threadCount);
	}

	public boolean connect(String host, int port) throws InterruptedException {
		try {
			if (handler != null) {
				handler.close();
			}
			handler = new NettyClientHandler(this);
			bootstrap = new Bootstrap();
			bootstrap.channel(NioSocketChannel.class);
			bootstrap.option(ChannelOption.SO_KEEPALIVE, true);
			bootstrap.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 1000);
			bootstrap.group(group);
			bootstrap.handler(new ChannelInitializer<SocketChannel>() {
				@Override
				protected void initChannel(SocketChannel ch) throws Exception {
					// 解决TCP粘包问题
					ch.pipeline().addLast(new LengthFieldBasedFrameDecoder(10 * 1024 * 1024, 10, 4));
					ch.pipeline().addLast(new NettyEncoder());
					ch.pipeline().addLast(new NettyDecoder());
					// 心跳检查
					int hearttime = SimpleNode.hearttime;
					idleStateHandler = new IdleStateHandler(hearttime * 3, hearttime * 2, hearttime * 2,
							TimeUnit.SECONDS);
					ch.pipeline().addLast(idleStateHandler);
					ch.pipeline().addLast(handler);
				}
			});

			this.handler.setId(host + ":" + port);
			channelFuture = bootstrap.connect(host, port).sync();
			if (channelFuture.isSuccess()) {
				logger.info("connect " + host + ":" + port + " success");
				this.handler.waitConnectSuccess();
				return true;
			} else {
				if (printLog) {
					logger.info("connect " + host + ":" + port + " fail");
				}
			}
			return false;
		} catch (Exception e) {
			if (printLog) {
				logger.info("connect " + host + ":" + port + " fail");
			}
			return false;
		}
	}

	public void close() throws Exception {
		if (handler != null) {
			handler.close();
		}

		if (channelFuture != null) {
			channelFuture.channel().closeFuture().sync();
		}

		if (group != null) {
			group.shutdownGracefully();
		}
	}

	public void setNodeHandler(Node node, NodeHandler nodeHandler) {
		this.node = (SimpleNode) node;
		this.nodeHandler = nodeHandler;
	}

	public void write(NodePacket packet) {
		this.handler.write(packet);
	}
}
